package org.swift.mashup.engine.concurrency.memory;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.swift.commons.xml.XmlUtils;
import org.w3c.dom.Node;

/**
 * @author hshober
 *
 */
@SuppressWarnings("unchecked")
public class CollectionMemoryItem extends MemoryItem {

	private final Collection collection;
	private MappedMemoryManager memoryManager;
		
	//protected use MemoryItem.newMemoryItem
	protected CollectionMemoryItem(int id, Collection item, MemoryType type) {
		super(id, type);
		this.collection = item;
	}
	protected CollectionMemoryItem(int id, MemoryType type, WorkingMemory memory) {
		super(id, type);
		this.collection = null;
		this.workingMemory = memory;
		if (memory.isPersistent()){
			this.memoryManager = memory.getWriteableMemory();
			createPersistentItem(id, null, type, this.memoryManager);
		}
	}
	
	protected CollectionMemoryItem(int id, MemoryType type, WorkingMemory memory, boolean exists) {
		super(id, type);
		this.collection = null;
		this.workingMemory = memory;
		if (exists) {
			mapPersistentItem(id, type);
		} else {
			createPersistentItem(id, null, type, memory.getWriteableMemory());
		}
	}
	private void mapPersistentItem(int id, MemoryType type){
		this.length = -1;
		this.position = -1;
	}
	private void createPersistentItem(int id, Object value, MemoryType type, MappedMemoryManager memoryManager){
		this.memoryManager = memoryManager;
		this.length = -1;
		this.position = -1;
		String mapRecord = id + "\t" + type.getName() + "\t" + this.position + "\t" + this.length + "\t" + "0" + "\n";
		try {
			memoryManager.getMapFileChannel().write(ByteBuffer.wrap(mapRecord.getBytes()));
			memoryManager.getMapFileChannel().force(true);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public Object getValue() {
		if (this.collection == null && this.type != null){
			Collection<MemoryItem> items = this.workingMemory.getItems(this.type.getCollectedTypeName());
			List values = extractValuesFromMemoryItems(items);			
			return values;
		}
		return collection;	
	}
	private List extractValuesFromMemoryItems(Collection<MemoryItem> items) {
		List list = new ArrayList();
		for (MemoryItem item : items) {
			list.add(item.getValue());
		}
		return list;		
	}

	@Override
	public Collection toCollection() {
		return collection;
	}

	@Override
	public Node toNode() {
		try {
			return XmlUtils.toDocument(this.toXML());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public String toXML() {
		StringBuffer strbuff = new StringBuffer();
		strbuff.append("<" + this.getType().getCollectedTypeName() + "s>");
		for (Object valueObject : (Collection)this.getValue()) {
			strbuff.append(this.toString(valueObject));
		}
		strbuff.append("</" + this.getType().getCollectedTypeName() + "s>");
		return strbuff.toString();
	}
	
	private String toString(Object value) {
		if (value instanceof Node) {
			return XmlUtils.toString((Node)value);
		} else if (value instanceof String) {
			return (String)value;
		} else if (value instanceof byte[]) {
			return new String((byte[])value);
		} else if (value instanceof Collection) {
			for (Object obj: (Collection)value) {
				this.toString(obj);
				// WTF? NO RETURN! Also, no assignment of the results of
				// the call to toString()! This will fall through to the throw
				// below anyway, so let's throw an exception now so that
				// at least someone knows what code didn't do what it
				// was supposed to do
				// FIXME: figure out what really should happen here and
				// make this block of code do that.
				throw new IllegalStateException("Please really implement me!");
			}
		} else if (value instanceof MemoryItem) {
			return ((MemoryItem)value).toXML();
		} else if (value == null) {
			return "";
		}
		
		if(value instanceof Throwable) {
			throw new RuntimeException("Unsupported type in collection :"
					+ value.getClass().getName(), (Throwable) value);		
		}
		else {
			throw new RuntimeException("Unsupported type in collection :"
					+ value.getClass().getName());		
		}
	}
	

	@Override
	public String toString() {
		return this.toXML();
	}

}
